package com.toe.recommend.webapi.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.toe.common.exception.ToeServiceException;
import com.toe.common.pojo.domain.ToeAuthenticationInfo;
import com.toe.common.restful.JsonPage;
import com.toe.common.restful.StateEnum;
import com.toe.pojo.dto.recommend.RecommendFoodDTO;
import com.toe.pojo.entity.recommend.RecommendFood;
import com.toe.pojo.entity.user.UserUsertbl;
import com.toe.pojo.vo.RecommendUserVO;
import com.toe.pojo.vo.recommend.RecommendFoodVO;
import com.toe.pojo.vo.user.UserUserVO;
import com.toe.recommend.service.RecommendFoodService;
import com.toe.recommend.webapi.mapper.RecommendFoodMapper;
import com.toe.user.service.UserUsertblService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 陶涛
 * @since 2022-07-19
 */
@Service
@DubboService
@Slf4j
public class RecommendFoodServiceImpl extends ServiceImpl<RecommendFoodMapper, RecommendFood> implements RecommendFoodService {
    private final static String HEALTHY = "healthy";
    private final static String UNDERWEIGHT = "underweight";
    private final static String OVERWEIGHT = "overweight";
    private final static String FAT = "fat";

    private static Integer pageNum1 = 1;
    private static Integer pageNum2 = 1;
    private static Integer pageNum3 = 1;
    private static Integer pageNum4 = 1;
    private static Integer pageNum5 = 1;
    private static Integer pageNum6 = 1;
    private static Integer pageNum7 = 1;
    private static Integer pageNum8 = 1;
    private static Integer pageNum9 = 1;
    private static final Integer pageSize = 30;

    @Autowired
    private RecommendFoodMapper recommendFoodMapper;

    @DubboReference
    private UserUsertblService usertblService;

    public List<List<RecommendFoodVO>> recommendByBMI(RecommendFoodDTO recommendFoodDTO) {
        RecommendUserVO recommendUser;
        Double bmi;
        if (recommendFoodDTO.getHeight()==null||recommendFoodDTO.getWeight()==null) {
            UserUserVO userDetail = usertblService.getUserDetail();
            recommendUser = new RecommendUserVO();
            BeanUtils.copyProperties(userDetail, recommendUser);
            bmi = recommendUser.getBmi();
        }
        else {
            recommendUser = new RecommendUserVO();
            recommendUser.setHeight(recommendFoodDTO.getHeight());
            recommendUser.setWeight(recommendFoodDTO.getWeight());
            recommendUser.setBmi(recommendFoodDTO.getWeight()/Math.pow(recommendFoodDTO.getHeight(),2));
            bmi = recommendUser.getBmi();
        }



        //根据bmi判断用户的健康状态
        if (bmi < 18) {
            recommendUser.setHealthyStatus(UNDERWEIGHT);
        } else if (bmi >= 18 && bmi <= 24) {
            recommendUser.setHealthyStatus(HEALTHY);
        } else if (bmi > 24 && bmi <= 29) {
            recommendUser.setHealthyStatus(OVERWEIGHT);
        } else {
            recommendUser.setHealthyStatus(FAT);
        }
        //获取用户健康状态
        String healthyStatus = recommendUser.getHealthyStatus();
        //根据bmi以及用户今天的体力活动计算出一天所需要得总热量
        double totalEnergy = 0.0;
        switch (healthyStatus) {
            case UNDERWEIGHT:
                switch (recommendFoodDTO.getTypeOfExercise()) {
                    case "极轻运动":
                        totalEnergy = recommendUser.getWeight() * 22.5;
                        break;
                    case "轻度运动":
                        totalEnergy = recommendUser.getWeight() * 35;
                        break;
                    case "中度运动":
                        totalEnergy = recommendUser.getWeight() * 40;
                        break;
                    case "重度运动":
                        totalEnergy = recommendUser.getWeight() * 42.5;
                        break;
                }
                break;
            case HEALTHY:
                switch (recommendFoodDTO.getTypeOfExercise()) {
                    case "极轻运动":
                        totalEnergy = recommendUser.getWeight() * 17.5;
                        break;
                    case "轻度运动":
                        totalEnergy = recommendUser.getWeight() * 27.5;
                        break;
                    case "中度运动":
                        totalEnergy = recommendUser.getWeight() * 35;
                        break;
                    case "重度运动":
                        totalEnergy = recommendUser.getWeight() * 40;
                        break;
                }
                break;
            case OVERWEIGHT:
            case FAT:
                switch (recommendFoodDTO.getTypeOfExercise()) {
                    case "极轻运动":
                        totalEnergy = recommendUser.getWeight() * 15;
                        break;
                    case "轻度运动":
                        totalEnergy = recommendUser.getWeight() * 22.5;
                        break;
                    case "中度运动":
                        totalEnergy = recommendUser.getWeight() * 30;
                        break;
                    case "重度运动":
                        totalEnergy = recommendUser.getWeight() * 35;
                        break;
                }
                break;
        }
        //根据身材管理计算总热量
        switch (recommendFoodDTO.getBodyManagement()) {
            case "增肌":
                totalEnergy *= 1.2;
                break;
            case "保持":
                totalEnergy *= 0.8;
                break;
            case "减肥":
                totalEnergy = healthyStatus.equals(OVERWEIGHT) ? totalEnergy * 0.65 : totalEnergy * 0.55;
                break;
        }

        //分别计算早中晚所需要的热量
        Double energyForBreakFast = totalEnergy * 0.3;
        Double energyForLunch = totalEnergy * 0.4;
        Double energyForDinner = totalEnergy * 0.3;
        recommendUser.setTotalEnergy(totalEnergy);
        recommendUser.setEnergyForBreakfast(energyForBreakFast);
        recommendUser.setEnergyForLunch(energyForLunch);
        recommendUser.setEnergyForDinner(energyForDinner);
        //获取推荐算法计算出来的早中晚餐
        List<RecommendFoodVO> recommendFoodBreakFastTemp = recommendBreakfast(energyForBreakFast, recommendFoodDTO.getBodyManagement(), recommendFoodDTO.getSpeciesForBreakfast());
        List<RecommendFoodVO> recommendFoodLunchTemp = recommendLunch(energyForLunch, recommendFoodDTO.getBodyManagement(), recommendFoodDTO.getSpeciesForLunch());
        List<RecommendFoodVO> recommendFoodDinnerTemp = recommendDinner(energyForDinner, recommendFoodDTO.getBodyManagement(), recommendFoodDTO.getSpeciesForDinner());
        //对食物进行去重
        List<RecommendFoodVO> recommendFoodBreakFast=new ArrayList<>();
        List<RecommendFoodVO> recommendFoodLunch=new ArrayList<>();
        List<RecommendFoodVO> recommendFoodDinner=new ArrayList<>();
        for (RecommendFoodVO breakFast:
             recommendFoodBreakFastTemp) {
            if (!recommendFoodBreakFast.contains(breakFast)){
                recommendFoodBreakFast.add(breakFast);
            }else{
                Double energy = breakFast.getEnergy();
                QueryWrapper<RecommendFood> wrapper=new QueryWrapper();
                wrapper.lt("energy",energy+10)
                        .gt("energy",energy-10);
                List<RecommendFood> foods = baseMapper.selectList(wrapper);
                int random = RandomUtils.nextInt(0, foods.size());
                RecommendFood food = foods.get(random);
                RecommendFoodVO foodVO=new RecommendFoodVO();
                BeanUtils.copyProperties(food,foodVO);
                foodVO.setType("早餐");
                recommendFoodBreakFast.add(foodVO);
            }
        }
        for (RecommendFoodVO lunch:
             recommendFoodLunchTemp) {
            if (!recommendFoodLunch.contains(lunch)){
                recommendFoodLunch.add(lunch);
            }else{
                Double energy = lunch.getEnergy();
                QueryWrapper<RecommendFood> wrapper=new QueryWrapper();
                wrapper.lt("energy",energy+10)
                        .gt("energy",energy-10);
                List<RecommendFood> foods = baseMapper.selectList(wrapper);
                int random = RandomUtils.nextInt(0, foods.size());
                RecommendFood food = foods.get(random);
                RecommendFoodVO foodVO=new RecommendFoodVO();
                BeanUtils.copyProperties(food,foodVO);
                foodVO.setType("午餐");
                recommendFoodLunch.add(foodVO);
            }
        }

        for (RecommendFoodVO dinner:
             recommendFoodDinnerTemp) {
            if (!recommendFoodDinner.contains(dinner)){
                recommendFoodDinner.add(dinner);
            }else{
                Double energy = dinner.getEnergy();
                QueryWrapper<RecommendFood> wrapper=new QueryWrapper();
                wrapper.lt("energy",energy+10)
                        .gt("energy",energy-10);
                List<RecommendFood> foods = baseMapper.selectList(wrapper);
                int random = RandomUtils.nextInt(0, foods.size());
                RecommendFood food = foods.get(random);
                RecommendFoodVO foodVO=new RecommendFoodVO();
                BeanUtils.copyProperties(food,foodVO);
                foodVO.setType("晚餐");
                recommendFoodDinner.add(foodVO);
            }
        }
//        将推荐的早中晚餐返回给前端
        List<List<RecommendFoodVO>> recommendFoods = new ArrayList<>();
        recommendFoods.add(recommendFoodBreakFast);
        recommendFoods.add(recommendFoodLunch);
        recommendFoods.add(recommendFoodDinner);
        return recommendFoods;
    }

    /**
     * 根据早餐所需的能量推荐早餐
     *
     * @param energyForBreakFast
     * @return 推荐早餐食物
     */
    public List<RecommendFoodVO> recommendBreakfast(Double energyForBreakFast, String bodyManagement, Integer species) {
        //获得早餐食物
        List<RecommendFood> breakfast = selectFoodByBodyManagement("breakfast", bodyManagement);
        log.info("获取的早餐集合:{}", breakfast);
        if (breakfast.isEmpty()) {
            throw new ToeServiceException(StateEnum.ERR_BAD_QUERY, "没有符合条件的早餐!");
        }
        Map<Double, Long> map = new HashMap<>();
        List<Double> energyList = new ArrayList<>();
        for (RecommendFood food :
                breakfast) {
            Double energy = food.getEnergy();
            Long foodId = food.getId();
            map.put(energy, foodId);
            energyList.add(energy);
        }
        List<RecommendFoodVO> recommendFoods = recommendFood(energyList, map, energyForBreakFast, species);
        //设置推荐的餐为早餐
        for (RecommendFoodVO food : recommendFoods
        ) {
            food.setType("早餐");
        }
        log.info("推荐的早餐:{}", recommendFoods);
        return recommendFoods;
    }


    /**
     * 根据晚餐所需的能量推荐午餐
     *
     * @param energyForLunch
     * @return 推荐的午餐食物
     */
    public List<RecommendFoodVO> recommendLunch(Double energyForLunch, String bodyManagement, Integer species) {
        //获得午餐食物
        List<RecommendFood> lunch = selectFoodByBodyManagement("lunch", bodyManagement);
        if (lunch.isEmpty()) {
            throw new ToeServiceException(StateEnum.ERR_BAD_QUERY, "没有符合条件的午餐");
        }
        log.info("获取的午餐集合:{}", lunch);
        Map<Double, Long> map = new HashMap<>();
        List<Double> energyList = new ArrayList<>();
        for (RecommendFood food :
                lunch) {
            Double energy = food.getEnergy();
            Long foodId = food.getId();
            map.put(energy, foodId);
            energyList.add(energy);
        }
        List<RecommendFoodVO> recommendFoods = recommendFood(energyList, map, energyForLunch, species);
        //设置推荐的餐为午餐
        for (RecommendFoodVO food : recommendFoods
        ) {
            food.setType("午餐");
        }
        log.info("推荐的午餐:{}", recommendFoods);
        return recommendFoods;
    }

    /**
     * 根据晚餐所需的能量推荐晚餐
     *
     * @param energyForDinner
     * @return 推荐晚餐食物
     */
    public List<RecommendFoodVO> recommendDinner(Double energyForDinner, String bodyManagement, Integer species) {
        //获得晚餐食物
        List<RecommendFood> dinner = selectFoodByBodyManagement("dinner", bodyManagement);
        if (dinner.isEmpty()) {
            throw new ToeServiceException(StateEnum.ERR_BAD_QUERY, "没有符合条件的晚餐!");
        }
        log.info("获取的晚餐集合:{}", dinner);
        Map<Double, Long> map = new HashMap<>();
        List<Double> energyList = new ArrayList<>();
        for (RecommendFood food :
                dinner) {
            Double energy = food.getEnergy();
            Long foodId = food.getId();
            map.put(energy, foodId);
            energyList.add(energy);
        }
        List<RecommendFoodVO> recommendFoods = recommendFood(energyList, map, energyForDinner, species);
        //设置推荐的餐为早餐
        for (RecommendFoodVO food : recommendFoods
        ) {
            food.setType("晚餐");
        }
        log.info("推荐的晚餐:{}", recommendFoods);
        return recommendFoods;
    }

    /**
     * 根据早中晚餐以及身材管理挑选食物
     *
     * @param type           早中晚餐
     * @param bodyManagement 身材管理
     * @return 筛选的食物
     */
    private List<RecommendFood> selectFoodByBodyManagement(String type, String bodyManagement) {
        //根据身材管理以及食物种类挑选食物
        //保持身材
        switch (type) {
            case "breakfast":
                switch (bodyManagement) {
                    case "保持":
                        PageHelper.startPage(pageNum1++, pageSize);
                        QueryWrapper<RecommendFood> queryWrapper = new QueryWrapper<>();
                        queryWrapper.in("category", "谷制品类", "谷类", "瓜果类", "鱼类", "虾类", "蛋类", "乳制品类", "乳类", "蛋制品类", "豆制品类")
                                .lt("fat", 12.0)
                                .gt("fat", 2.0)
                                .lt("protein", 12.0)
                                .gt("protein", 2.0);
                        List<RecommendFood> recommendFoods = recommendFoodMapper.selectList(queryWrapper);
                        log.info("未分页的数据:{}", recommendFoods);
                        JsonPage<RecommendFood> page = JsonPage.restPage(new PageInfo<>(recommendFoods));
                        log.info("当前页码:{}", page.getPageNum());
                        log.info("总页数:{}", page.getTotalPages());
                        log.info("总条数:{}", page.getTotalCount());
                        log.info("当前页集合[]>>>:{}", page.getList());
                        List<RecommendFood> breakfast = page.getList();
                        Integer totalPages = page.getTotalPages();
                        log.info("pageNum1>>>{}", pageNum1);
                        if (totalPages - pageNum1 == 1 || pageNum1 >= totalPages) {
                            pageNum1 = 1;
                        }

                        log.info("分页数据:{}", breakfast);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(breakfast);
                        }
                        return breakfast;
                    case "增肌":
                        PageHelper.startPage(pageNum2++, pageSize);
                        //挑选高蛋白食物(蛋白质(含量大于12) 碳水化合物 脂肪(大于10克) 矿物质及维生素)
                        QueryWrapper<RecommendFood> queryWrapper1 = new QueryWrapper<>();
                        queryWrapper1.in("category", "畜肉类", "瓜果类", "蛋制品类", "谷类", "乳类", "乳制品类", "谷制品类", "虾类")
                                .ge("protein", 12.0)
                                .ge("fat", 10.0);
                        List<RecommendFood> recommendFoods1 = recommendFoodMapper.selectList(queryWrapper1);
                        JsonPage<RecommendFood> page1 = JsonPage.restPage(new PageInfo<>(recommendFoods1));
                        List<RecommendFood> breakfast1 = page1.getList();
                        Integer totalPages1 = page1.getTotalPages();
                        if (totalPages1 - pageNum2 == 1 || pageNum2 >= totalPages1) {
                            pageNum2 = 1;
                        }
                        log.info("分页数据:{}", breakfast1);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(breakfast1);
                        }
                        return breakfast1;
                    case "减肥":
                        PageHelper.startPage(pageNum3++, pageSize);
                        //挑选高蛋白食物(蛋白质(含量大于12) 碳水化合物 脂肪(大于10克) 矿物质及维生素)
                        QueryWrapper<RecommendFood> queryWrapper2 = new QueryWrapper<>();
                        queryWrapper2.in("category", "豆制品类", "瓜果类", "薯类", "粥类", "谷制品类", "乳类")
                                .lt("fat", 6.0)
                                .lt("sugar", 50.0);
                        List<RecommendFood> recommendFoods2 = recommendFoodMapper.selectList(queryWrapper2);
                        JsonPage<RecommendFood> page2 = JsonPage.restPage(new PageInfo<>(recommendFoods2));
                        List<RecommendFood> breakfast2 = page2.getList();
                        Integer totalPages2 = page2.getTotalPages();
                        if (totalPages2 - pageNum3 == 1 || pageNum3 >= totalPages2) {
                            pageNum3 = 1;
                        }
                        log.info("分页数据:{}", breakfast2);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(breakfast2);
                        }
                        return breakfast2;
                }
                break;
            case "lunch":
                switch (bodyManagement) {
                    case "保持":
                        PageHelper.startPage(pageNum4++, pageSize);
                        QueryWrapper<RecommendFood> queryWrapper = new QueryWrapper<>();
                        queryWrapper.in("category", "谷类", "软体动物类", "虾类", "鱼类", "蔬菜类", "畜肉类")
                                .lt("fat", 12.0)
                                .ge("fat", 3.0)
                                .ge("protein", 3);
                        List<RecommendFood> recommendFoods = recommendFoodMapper.selectList(queryWrapper);
                        JsonPage<RecommendFood> page = JsonPage.restPage(new PageInfo<>(recommendFoods));
                        List<RecommendFood> lunch = page.getList();
                        Integer totalPages = page.getTotalPages();
                        if (totalPages - pageNum4 == 1 || pageNum4 >= totalPages) {
                            pageNum4 = 1;
                        }
                        log.info("分页数据:{}", lunch);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(lunch);
                        }
                        return lunch;
                    case "增肌":
                        PageHelper.startPage(pageNum5++, pageSize);
                        //挑选高蛋白食物(蛋白质(含量大于12) 碳水化合物 脂肪(大于10克) 矿物质及维生素)
                        QueryWrapper<RecommendFood> queryWrapper1 = new QueryWrapper<>();
                        queryWrapper1.in("category", "畜肉类", "瓜果类", "蛋制品类", "蛋类", "谷类", "乳类", "乳制品类", "谷制品类", "虾类")
                                .ge("protein", 12.0)
                                .ge("fat", 10.0);
                        List<RecommendFood> recommendFoods1 = recommendFoodMapper.selectList(queryWrapper1);
                        JsonPage<RecommendFood> page1 = JsonPage.restPage(new PageInfo<>(recommendFoods1));
                        List<RecommendFood> lunch1 = page1.getList();
                        Integer totalPages1 = page1.getTotalPages();
                        if (totalPages1 - pageNum5 == 1 || pageNum5 >= totalPages1) {
                            pageNum5 = 1;
                        }
                        log.info("分页数据:{}", lunch1);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(lunch1);
                        }
                        return lunch1;
                    case "减肥":
                        PageHelper.startPage(pageNum6++, pageSize);
                        QueryWrapper<RecommendFood> queryWrapper2 = new QueryWrapper<>();
                        queryWrapper2.in("category", "豆类", "蔬菜类", "谷类", "水果类", "其他肉类", "畜肉类", "禽肉类", "瓜果", "蛋类", "谷制品类")
                                .lt("fat", 8.0)
                                .lt("sugar", 50.0);
                        List<RecommendFood> recommendFoods2 = recommendFoodMapper.selectList(queryWrapper2);
                        JsonPage<RecommendFood> page2 = JsonPage.restPage(new PageInfo<>(recommendFoods2));
                        List<RecommendFood> lunch2 = page2.getList();
                        Integer totalPages2 = page2.getTotalPages();
                        if (totalPages2 - pageNum6 == 1 || pageNum6 >= totalPages2) {
                            pageNum6 = 1;
                        }
                        log.info("分页数据:{}", lunch2);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(lunch2);
                        }
                        return lunch2;
                }
                break;
            case "dinner":
                switch (bodyManagement) {
                    case "保持":
                        PageHelper.startPage(pageNum7++, pageSize);
                        QueryWrapper<RecommendFood> queryWrapper = new QueryWrapper<>();
                        queryWrapper.in("category", "谷类", "蔬菜类", "豆制品类", "畜肉制品类", "禽类制品类", "其他肉类", "虾类", "饮料")
                                .lt("fat", 12.0)
                                .gt("protein", 3.0);
                        List<RecommendFood> recommendFoods = recommendFoodMapper.selectList(queryWrapper);
                        JsonPage<RecommendFood> page = JsonPage.restPage(new PageInfo<>(recommendFoods));
                        List<RecommendFood> dinner = page.getList();
                        Integer totalPages = page.getTotalPages();
                        if (totalPages - pageNum7 == 1 || pageNum7 >= totalPages) {
                            pageNum7 = 1;
                        }
                        log.info("分页数据:{}", dinner);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(dinner);
                        }
                        return dinner;
                    case "增肌":
                        //挑选高蛋白食物(蛋白质(含量大于12) 碳水化合物 脂肪(大于10克) 矿物质及维生素)
                        PageHelper.startPage(pageNum8++, pageSize);
                        QueryWrapper<RecommendFood> queryWrapper1 = new QueryWrapper<>();
                        queryWrapper1.in("category", "软体动物类", "畜肉类", "畜肉制品类", "豆类", "瓜果类", "蛋制品类", "蛋类", "谷类", "乳类", "乳制品类", "谷制品类", "虾类")
                                .ge("protein", 12.0)
                                .ge("fat", 10.0);
                        List<RecommendFood> recommendFoods1 = recommendFoodMapper.selectList(queryWrapper1);
                        JsonPage<RecommendFood> page1 = JsonPage.restPage(new PageInfo<>(recommendFoods1));
                        List<RecommendFood> dinner1 = page1.getList();
                        Integer totalPages1 = page1.getTotalPages();
                        if (totalPages1 - pageNum8 == 1 || pageNum8 >= totalPages1) {
                            pageNum8 = 1;
                        }
                        log.info("分页数据:{}", dinner1);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(dinner1);
                        }
                        return dinner1;
                    case "减肥":
                        PageHelper.startPage(pageNum9++, pageSize);
                        QueryWrapper<RecommendFood> queryWrapper2 = new QueryWrapper<>();
                        queryWrapper2.in("category", "坚果类", "豆类", "其他肉类", "虾类", "鱼类", "软体动物类", "瓜果类", "蔬菜类", "菌类", "水果")
                                .lt("fat", 8.0)
                                .lt("sugar", 50.0);
                        List<RecommendFood> recommendFoods2 = recommendFoodMapper.selectList(queryWrapper2);
                        JsonPage<RecommendFood> page2 = JsonPage.restPage(new PageInfo<>(recommendFoods2));
                        List<RecommendFood> dinner2 = page2.getList();
                        Integer totalPages2 = page2.getTotalPages();
                        if (totalPages2 - pageNum9 == 1 || pageNum9 >= totalPages2) {
                            pageNum9 = 1;
                        }
                        log.info("分页数据:{}", dinner2);
                        for (int i = 0; i < 10; i++) {
                            Collections.shuffle(dinner2);
                        }
                        return dinner2;
                }
                break;
        }
        return null;
    }

    /**
     * 根据算法得到的食物id获取推荐食物
     *
     * @param energyList 食物列表
     * @param map        能量值对应的id
     * @param energy     能量
     * @return
     */
    private List<RecommendFoodVO> recommendFood(List<Double> energyList, Map<Double, Long> map, Double energy, Integer species) {
        List<Double> minSub = getMinSub(energyList, energy, species);
        //获取最接近的组合
        //获得子集
        Double[] array = energyList.toArray(new Double[0]);
        List<List<Double>> arr = combiantion(array, species);
//      最接近的组合为:
        List<Double> list = arr.get(minSub.indexOf(Collections.min(minSub)));
        //根据组合获得食物id
        List<RecommendFoodVO> recommendFoods = new ArrayList<>();
        for (Double e :
                list) {
            Long foodId = map.get(e);
            RecommendFood recommendFood = recommendFoodMapper.selectById(foodId);
            RecommendFoodVO recommendFoodVO = new RecommendFoodVO();
            BeanUtils.copyProperties(recommendFood, recommendFoodVO);
            recommendFoods.add(recommendFoodVO);
        }

        return recommendFoods;
    }

    /**
     * 计算热量得到最符合条件的食物
     *
     * @param list
     * @param target
     * @return 子集的和与目标能量的差的数组
     */
    private List<Double> getMinSub(List<Double> list, Double target, Integer species) {
        List<List<Double>> arr = combiantion(list.toArray(new Double[0]), species);
        List<Double> sum = new ArrayList<>();
        Double result = 0.0;
        for (int i = 0; i < arr.size(); i++) {
            for (int j = 0; j < arr.get(i).size(); j++) {
                if (arr.get(i).size() < 3)
                    continue;
                result += arr.get(i).get(j);
            }
            sum.add(result);
            result = 0.0;
        }
        List<Double> sub = new ArrayList<>();
        for (Double num :
                sum) {
            result = Math.abs(num - target);
            sub.add(result);
        }
//        System.out.println(arr);
//        System.out.println(sum);
//        System.out.println(sub);
        System.out.println("最小值为:" + Collections.min(sub));
        System.out.println("最小值的下标为" + sub.indexOf(Collections.min(sub)));
        System.out.println("最接近的组合为:" + arr.get(sub.indexOf(Collections.min(sub))));
        return sub;
    }

    public static <Double> List<List<Double>> combiantion(Double[] datas, int number) {
        if ((datas == null) || (datas.length == 0) || (datas.length < number)) {
            return null;
        }
        List<List<Double>> combines = new ArrayList<List<Double>>();
        List<Double> temps = new ArrayList<Double>();

        combine(datas, 0, number, temps, combines);
        return combines;
    }

    public static <Double> void combine(Double[] datas, int begin, int number, List<Double> temps, List<List<Double>> combines) {
        if (number == 0) {
            List<Double> aCombine = new ArrayList<>();
            aCombine.addAll(temps);
            combines.add(aCombine);
            return;
        }
        if (begin == datas.length) {
            return;
        }
        temps.add(datas[begin]);
        combine(datas, begin + 1, number - 1, temps, combines);
        temps.remove(datas[begin]);
        combine(datas, begin + 1, number, temps, combines);
    }


//    /**
//     * 通过热量推荐获取数组子集
//     *
//     * @param list
//     * @return
//     */
//    private static List<List<Double>> getArr(List<Double> list) {
//        if (list.size() > 0) {//当数组长度大于0时才进行子集获取
//            List<List<Double>> result = new ArrayList<>();
//            long n = (long) Math.pow(2, list.size());//集合的子集数量  2的数组长度次方
//            List<Double> temp;
//            for (long l = 0L; l < n; l++) {//遍历出的子集的个数
//                temp = new ArrayList<>();
//                for (int i = 0; i < list.size(); i++) {
//                    if ((l >>> i & 1) == 1) {
//                        temp.add(list.get(i));//用一个List<Integer>来装每个子集中的元素
//                    }
//                }
//                if (temp.size() == 5) {
//                    result.add(temp);
//                }//当获取到的子集不为空时就放入返回结果中
//                else if (temp.size() > 5) {
//                    break;
//                }
//
//            }
//            return result;
//        }
//        System.out.println("我为null了!");
//        return null;
//    }

}
